/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package technotheist.imgcog.feedfwd;

import technotheist.imgcog.SupervisedNet;

/**
 *
 * @author Mitch
 */
public class FeedFwdNet extends SupervisedNet {
    
    

    public static class FeedFwdNetConfig implements NetConfig {

        @Override
        public int getLayerCount() {
            throw new UnsupportedOperationException("Not supported yet.");
        }

        @Override
        public int getLayerSize(int layer) {
            throw new UnsupportedOperationException("Not supported yet.");
        }

        @Override
        public int getWeightsPerNode(int layer) {
            throw new UnsupportedOperationException("Not supported yet.");
        }

        @Override
        public int getTotalWeightCount() {
            throw new UnsupportedOperationException("Not supported yet.");
        }

        @Override
        public int getTotalNodeCount() {
            throw new UnsupportedOperationException("Not supported yet.");
        }

        @Override
        public int getNumWeights(int i) {
            throw new UnsupportedOperationException("Not supported yet.");
        }

        @Override
        public boolean sharedBias() {
            throw new UnsupportedOperationException("Not supported yet.");
        }

        private float getLearnQ() {
            throw new UnsupportedOperationException("Not yet implemented");
        }
    }

    protected abstract class FeedFwdNetRunner extends SupervisedNetRunner {

        private final byte Mode[] = new byte[]{0};
        private final float Q;

        public FeedFwdNetRunner(FeedFwdNetConfig conf) {
            super(conf);
            Q = conf.getLearnQ();
        }

        @Override
        public void run() {
            if (Mode[0] == 0) {
                feedFwd(getPassId());
            }
            if (Mode[0] == 1) {
                backProp(layerCount - getPassId());
            }
        }

        protected void feedFwd(int layeri) {
            int node = getGlobalId();
            float sum = 0;
            int len = getLayerSize(layeri);
            int w0 = getInputWeightIndex(layeri, node, 0);
            for (int i = 0; i < len; i += 1) {
                sum += getWeight(w0 + i) * getNodeStore(layeri, i);
            }
            setNodeStore(layeri + 1, node, f(sum));
        }
        int T[];

        protected void backProp(int layerj) {
            int ni = getGlobalId();
            float sumErr = 0;
            float dt = 1f / (1f + T[0]);
            int len = getLayerSize(layerj);
            for (int nj = 0; nj < len; nj += 1) {
                sumErr += bp(layerj, ni, nj, getInputWeightIndex(layerj - 1, ni, nj), dt);
            }
            this.setNodeError(layerj - 1, ni, sumErr);
        }

        protected final float bp(int layerj, int ni, int nj, int wInd, float dt) {
            float x = 0;
            float e = 0;
            float delta = 0;
            //
            float dwa0 = 0;
            float mu0 = 0;
            float dwa1 = 0;
            float mu1 = 0;
            float dw = 0;
            //
            x = getNodeStore(layerj, nj);
            e = getNodeError(layerj, nj);
            delta = e * df(x);


            dwa0 = getDWA(wInd);
            mu0 = getMu(wInd);
            //
            dw = mu0 * delta * x;
            dwa1 = (dw * dt) + (dwa0 * (1f - dt));
            mu1 = mu0 * max(0.5f, Q * dw * (dwa0 / (dwa1 * dwa1)));

            float ce = getWeight(wInd) * delta;
            addWeight(wInd, dw);
            setDWA(wInd, dwa1);
            setMu(wInd, mu1);
            return ce;
        }

        @Override
        protected int getInputWeightIndex(int layer, int node, int w) {
            return getWeightStart(layer - 1) + node * getLayerSize(layer - 1) + w;
        }

        protected abstract float f(float x);

        protected abstract float df(float fx);
    }
}
